package org.codefinger.json.parser.deserializer;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.codefinger.json.JSONBase;
import org.codefinger.json.parser.JSONArrayDeserializer;
import org.codefinger.json.parser.JSONDeserializer;
import org.codefinger.json.parser.JSONLexer;
import org.codefinger.json.parser.JSONObjectDeserializer;
import org.codefinger.json.util.magic.MagicField;
import org.codefinger.json.util.magic.MagicMethod;

public class DeserializerForEnum implements JSONDeserializer {

	private Map<Object, Object>	enumNameMap		= new HashMap<Object, Object>();

	private Map<Object, Object>	enumOrdinalMap	= new HashMap<Object, Object>();

	public DeserializerForEnum(Class<?> enumClass) {
		MagicMethod ordinalMethod = MagicMethod.getMagicMethod(enumClass, "ordinal");
		MagicMethod nameMethod = MagicMethod.getMagicMethod(enumClass, "name");
		Map<Object, Object> enumNameMap = this.enumNameMap;
		Map<Object, Object> enumOrdinalMap = this.enumOrdinalMap;
		int modifiers;
		for (Field field : enumClass.getFields()) {
			modifiers = field.getModifiers();
			if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) {
				Object value = MagicField.getField(field).getValue(null);
				if (value instanceof Enum) {
					enumNameMap.put(nameMethod.invoke(value).toString().toUpperCase(), value);
					enumOrdinalMap.put(ordinalMethod.invoke(value), value);
				}
			}
		}
	}

	@Override
	public Object stringValue(String value) {
		return enumNameMap.get(value.toUpperCase());
	}

	@Override
	public Object stringValue(JSONLexer jsonLexer) {
		return enumNameMap.get(jsonLexer.getJSONString().toUpperCase());
	}

	@Override
	public Object intValue(String value) {
		return enumOrdinalMap.get(Integer.valueOf(value));
	}

	@Override
	public Object floatValue(String value) {
		return enumOrdinalMap.get(new BigInteger(value).intValue());
	}

	@Override
	public Object falseValue() {
		return null;
	}

	@Override
	public Object trueValue() {
		return null;
	}

	@Override
	public JSONArrayDeserializer createArrayDeserializer() {
		return ArrayDeserializerForNull.INSTANCE;
	}

	@Override
	public JSONObjectDeserializer createObjectDeserializer() {
		return ObjectDeserializerForNull.INSTANCE;
	}

	@Override
	public Object value(Integer value) {
		return enumOrdinalMap.get(value);
	}

	@Override
	public Object value(Double value) {
		return enumOrdinalMap.get(value.intValue());
	}

	@Override
	public Object value(Float value) {
		return enumOrdinalMap.get(value.intValue());
	}

	@Override
	public Object value(Long value) {
		return enumOrdinalMap.get(value.intValue());
	}

	@Override
	public Object value(Short value) {
		return enumOrdinalMap.get(value.intValue());
	}

	@Override
	public Object value(Byte value) {
		return enumOrdinalMap.get(value.intValue());
	}

	@Override
	public Object value(Boolean value) {
		return enumOrdinalMap.get(value ? 1 : 0);
	}

	@Override
	public Object value(Character value) {
		return enumOrdinalMap.get((int) value.charValue());
	}

	@Override
	public Object value(Date value) {
		return enumOrdinalMap.get((int) value.getTime());
	}

	@Override
	public Object value(BigDecimal value) {
		return enumOrdinalMap.get(value.intValue());
	}

	@Override
	public Object value(BigInteger value) {
		return enumOrdinalMap.get(value.intValue());
	}

	@Override
	public Object value(JSONBase value) {
		switch (value.jsonType()) {
		case Boolean:
		case Number:
		case Date:
			return enumOrdinalMap.get(value.intValue());
		default:
			return enumNameMap.get(value.stringValue());
		}
	}
}
